package com.melichercik.code;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import android.util.Log;

public class CodeCompiler  {
	
	private ArrayList<Brick> code = new ArrayList<Brick>();
	private int currentLoopDepth = 0;
	private HashMap<Brick, Brick> gosubThen = new HashMap<Brick, Brick>();
	private HashMap<Brick, Brick> gosubElse = new HashMap<Brick, Brick>();
	private HashMap<Brick, Brick> gosubThenEnd = new HashMap<Brick, Brick>();
	private HashMap<Brick, Brick> gosubElseEnd = new HashMap<Brick, Brick>();
	private HashMap<Brick, Brick> whiles = new HashMap<Brick, Brick>();
	private HashMap<Brick, String> whileConds = new HashMap<Brick, String>();
	private HashSet<Brick> resolvedIfs = new HashSet<Brick>();
	
	public CodeCompiler(ArrayList<Brick> code) {
		this.code = code;
	}
	
	public String getCode() {
		String result = "";
		ArrayList<String> codeLines = getCodeLines();
		
		for(String line : codeLines) {
			result += line + System.getProperty("line.separator");
		}
		
		return result;
	}
	
	private ArrayList<String> getCodeLines() {
		
		prepareBricks();
		
		while(replaceBranches()) {
		}
		
		ArrayList<String> codeLines = new ArrayList<String>();
		
		int lineNumb = 10;
		
		for(Brick brick: code) {
			if (brick.getType() == Brick.START) {
				continue;
			}
			
			String line = lineNumb + " " + brickToString(brick);
			
			codeLines.add(line);
			
			lineNumb += 10;
		}
		
		codeLines = addGosubsJumps(codeLines);
		
		return codeLines;
	} 
	
	//doplnenie nastavovanie smeru a vzhodenie zaciatkov whileov
	private void prepareBricks() {
		ArrayList<Brick> tmpCode = new ArrayList<Brick>();
		
		for(Brick brick: code) {
			if (brick.getType() == Brick.WHILE) {
				whiles.put(((LoopBrick) brick).end, code.get(code.indexOf(brick) + 1));
				whileConds.put(((LoopBrick) brick).end, ((LoopBrick) brick).var1);
			} else {
				tmpCode.add(brick);
			}
			
			if (brick.getType() == Brick.START) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.VARIABLE);
				tmpBrick.var1 = "w";
				tmpBrick.var2 = "0";
				tmpCode.add(tmpBrick);
			} else if (brick.getType() == Brick.ROLL) {
				SimpleBrick tmpBrick = new SimpleBrick(Brick.VARIABLE);
				tmpBrick.var1 = "w";
				tmpBrick.var2 = ((SimpleBrick) brick).var1;
				tmpCode.add(tmpBrick);
			}
		}
		
		code = tmpCode;
	}
	
	private boolean replaceBranches() {
		IfBrick ifBrick = null;
		boolean unresolvedIf = false;
		ArrayList<Brick> tmpCode = new ArrayList<Brick>();
		ArrayList<Brick> tmpThen = new ArrayList<Brick>();
		ArrayList<Brick> tmpElse = new ArrayList<Brick>();
		ArrayList<Brick> activeRecord = tmpCode;

		for(Brick brick: code) {
			
			activeRecord.add(brick);
			
			if (!unresolvedIf && brick.getType() == Brick.IF && !resolvedIfs.contains(brick)) {
				resolvedIfs.add(brick);
				unresolvedIf = true;
				ifBrick = ((IfBrick) brick);
				
			} else if (unresolvedIf && brick.getType() == Brick.THEN && brick == ifBrick.then) {
				activeRecord.remove(brick);
				activeRecord = tmpThen;
				
			} else if (unresolvedIf && brick.getType() == Brick.ENDTHEN && brick == ifBrick.endthen) {
				activeRecord.remove(brick);
				
			} else if (unresolvedIf && brick.getType() == Brick.ELSE && brick == ifBrick.els) {
				activeRecord.remove(brick);
				activeRecord = tmpElse;
				
			} else if (unresolvedIf && brick.getType() == Brick.ENDELSE && brick == ifBrick.endels) {
				activeRecord.remove(brick);
				
			} else if (unresolvedIf && brick.getType() == Brick.ENDIF && brick == ifBrick.endif) {
				activeRecord.remove(brick);
				activeRecord = tmpCode;
			}
		}
		
		if (unresolvedIf) {
			Brick thenReturn = new Brick(Brick.RETURN);
			Brick elseReturn = new Brick(Brick.RETURN);
			
			tmpThen.add(thenReturn);
			tmpElse.add(elseReturn);
			
			gosubThen.put(ifBrick, tmpThen.get(0));
			gosubElse.put(ifBrick, tmpElse.get(0));
			
			gosubThenEnd.put(ifBrick, thenReturn);
			gosubElseEnd.put(ifBrick, elseReturn);
			
			tmpCode.addAll(tmpThen);
			tmpCode.addAll(tmpElse);
		}
		
		code = tmpCode;
		
		return unresolvedIf;
		
	}
	
	private ArrayList<String> addGosubsJumps(ArrayList<String> codeLines) {
		
		return codeLines;
	}
	
	private String brickToString(Brick brick) {
		String result = "";
		String brickType = brick.getType();
		
		if (brickType == Brick.ROLL) {
			SimpleBrick tmpBrick = ((SimpleBrick) brick);
			
			result = "goroll " + tmpBrick.var1 + ", " + tmpBrick.var2 + ", 1";
			
		} else if (brickType == Brick.STOP) {
			result = "goroll w, 0, 2";
			
		} else if (brickType == Brick.COLOR) {
			SimpleBrick tmpBrick = ((SimpleBrick) brick);
			result = "RGB " + tmpBrick.var1 + ", " + tmpBrick.var2 + ", " + tmpBrick.var3;
			
		} else if (brickType == Brick.LED) {
			SimpleBrick tmpBrick = ((SimpleBrick) brick);
			result = "backLED " + tmpBrick.var1;
			
		} else if (brickType == Brick.DELAY) {
			SimpleBrick tmpBrick = ((SimpleBrick) brick);
			result = "delay " + tmpBrick.var1;
			
		} else if (brickType == Brick.PRINT) {
			SimpleBrick tmpBrick = ((SimpleBrick) brick);
			result = "print \"" + tmpBrick.var1 + "\"";
			
		} else if (brickType == Brick.SOUND) {
			result = "print \"playSoundMessage\"";
			
		} else if (brickType == Brick.VARIABLE) {
			SimpleBrick tmpBrick = ((SimpleBrick) brick);
			result = tmpBrick.var1 + " = " + tmpBrick.var2;
			
		} else if (brickType == Brick.LOOP) {
			String loopVar = "";
			if (currentLoopDepth == 0) {
				loopVar = "X";
			} else if(currentLoopDepth == 1) {
				loopVar = "Y";
			} else {
				loopVar = "Z";
			}
			
			currentLoopDepth++;
			
			LoopBrick tmpBrick = ((LoopBrick) brick);
			result = "for " + loopVar + " = " + tmpBrick.var1 + " to " + tmpBrick.var2 + " step " + tmpBrick.var3;
		
		} else if (brickType == Brick.ENDLOOP) {
			String loopVar = "";
			if (currentLoopDepth == 1) {
				loopVar = "X";
			} else if(currentLoopDepth == 2) {
				loopVar = "Y";
			} else {
				loopVar = "Z";
			}
			
			currentLoopDepth--;
			
			result = "next " + loopVar;
		
		} else if (brickType == Brick.ENDWHILE) {
			int line = code.indexOf(whiles.get(brick)) * 10;
			result = "if " + whileConds.get(brick) + " then goto " + line;
		
		} else if (brickType == Brick.IF) {
			IfBrick tmpBrick = ((IfBrick) brick);
			int thenLine = code.indexOf(gosubThen.get(tmpBrick)) * 10;
			int elseLine = code.indexOf(gosubElse.get(tmpBrick)) * 10;
			String thenJump = "goto " + Integer.toString(thenLine);
			String elseJump = "goto " + Integer.toString(elseLine);
			
			result = "if " + tmpBrick.condition + " then " + thenJump + " else " + elseJump;
			
		} else if (brickType == Brick.RETURN) {
			result = "goto " + getGoToLine(brick);	
			
		} else if (brickType == Brick.END) {
			result = "end";
		}
		
		
		return result;
	}
	
	private String getGoToLine(Brick brick) {
		
		Log.d("MyDebug", Integer.toString(gosubThenEnd.size()));
		
		Iterator it = gosubThenEnd.entrySet().iterator();
	    while (it.hasNext()) {
	        Map.Entry pairs = (Map.Entry)it.next();
	        Brick key = (Brick) pairs.getKey();
	        Brick value = (Brick) pairs.getValue();
	        if (value == brick) {
	        	
	        	int line = (code.indexOf(key) + 1) * 10;
	        	
	        	return Integer.toString(line);
	        }
	    }
	    
	    it = gosubElseEnd.entrySet().iterator();
	    while (it.hasNext()) {
	        Map.Entry pairs = (Map.Entry)it.next();
	        Brick key = (Brick) pairs.getKey();
	        Brick value = (Brick) pairs.getValue();
	        if (value == brick) {
	        	
	        	int line = (code.indexOf(key) + 1) * 10;
	        	
	        	return Integer.toString(line);
	        }
	    }
		
		return "";
	} 
	
}
